package abg.core.common.security;


import abg.core.common.properties.AbgProSecurity;
import abg.core.common.util.JwtKit;
import abg.core.common.util.MathKit;
import abg.core.common.util.coll.ArrayKit;
import abg.core.common.util.http.RequestKit;
import abg.core.domain.sys.MSG;
import abg.core.domain.sys.ServiceException;
import abg.core.service.sys.SysResService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.util.ArrayList;
import java.util.List;

/**
 * 权限拦截
 *
 * @author 王永吉
 */
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Resource
    private AbgProSecurity abgProSecurityProperties;
    @Resource
    private SysResService sysResService;

    //资源匹配对象
    public static final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Headers", "authorization,Authorization,Token,Auth,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type");

        //放行OPTIONS
        String requestMethod = request.getMethod();
        if (requestMethod.equals("OPTIONS")) return true;


        if (handler instanceof HandlerMethod) {
            //注解优先：先检查注解
            if (handlerAuth((HandlerMethod) handler)) return true;

            return extracted(request);

        }

        return true;
    }

    private boolean extracted(HttpServletRequest request) {
        //
        //判断：公开资源，不需要登录
        String requestURI = request.getRequestURI();
        if (isIgnore(requestURI)) return true;

        //
        //检查Token
        String token = request.getHeader("Token");
        if (token == null || token.trim().equals("")) {
            log.error(MSG.NOT_TOKEN.message() + " requestURI = " + requestURI);
            throw new ServiceException(MSG.NOT_TOKEN);
        }
        JwtKit.check(token);

        //
        //获取用户信息，判断角色是否具备 URL访问资格.
        AuthUser currentUser = AuthKit.getCurrentUser(token);
        if (currentUser == null) throw new ServiceException(MSG.ER_410);

        //
        //超级用户放行
        if (currentUser.isRoot()) return true;
        //

        //
        //检查角色和部门 是否具备资源的访问权限
        boolean hasRes = sysResService.checkHasRes(requestURI, currentUser.getRoleIds(),currentUser.getOrgIds());
        if (hasRes) return true;

        //
        //权限不足
        log.error("权限不足 requestURI:{} ；userAccount:{}", requestURI, currentUser.getAccount());

        throw new ServiceException(MSG.ER_403);
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception) {
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView) {
    }

    /**
     * 匹配放行URI
     */
    private boolean isIgnore(String requestURI) {
        List<String> uriList = abgProSecurityProperties.getResIgnore();
        for (String ignoreUrl : uriList) {
            boolean match = pathMatcher.match(ignoreUrl, requestURI);
            if (match) return true;
        }
        return false;
    }

    //注解上的权限
    public boolean handlerAuth(HandlerMethod handlerMethod) {
        //TODO 注解资源在项目启动成功前扫描完成： HashMap<ResKey,Auth> 存到本地即可
        Auth classAuth = handlerMethod.getBeanType().getAnnotation(Auth.class);
        Auth methodAuth = handlerMethod.getMethod().getAnnotation(Auth.class);
        Auth auth = null;

        if (classAuth != null) auth = classAuth;
        if (methodAuth != null) auth = methodAuth;
        if (auth == null) return false;

        //放行OPTIONS
        HttpServletRequest request = RequestKit.currentRequest();
        String method = request.getMethod();
        if (method.equals("OPTIONS")) return true;

        //判断：忽略资源，不需要检查
        boolean isIgnore = auth.ignore();
        if (isIgnore) return true;

        //只需要登录，即可放行
        boolean isOnlyLogin = auth.onlyLogin();
        String token = request.getHeader("Token");
        AuthUser currentUser = AuthKit.getCurrentUser();
        String accessTokenMd5 = MathKit.getMD5Upper(token);

        //        if (loginType.equals("APP")) {
        //            cache.set(CacheKey.USER_LOGIN_APP + userInfo.getId(), accessTokenMd5);
        //        }
        //        if (loginType.equals("WEB")) {
        //            cache.set(CacheKey.USER_LOGIN_WEB + userInfo.getId(), accessTokenMd5);
        //        }

        if (isOnlyLogin) return true;
        if (currentUser == null) throw new ServiceException(MSG.ER_410);

        //超级用户放行
        if (currentUser.isRoot()) return true;

        //访问者具备的角色
        List<String> userRoleIds = currentUser.getRoleIds();
        String requestURI = request.getRequestURI();

        //要求必须排除的角色
        ArrayList<String> mastExcludeRoleIds = new ArrayList<>(ArrayKit.asList(auth.excludeRole()));
        for (String excludeRoleId : mastExcludeRoleIds) {
            if (userRoleIds.contains(excludeRoleId)) {
                log.error("【权限不足：角色被排除：{}】requestURI:{}  userAccount:{}  userRoleIds:{}", excludeRoleId, requestURI, currentUser.getAccount(), userRoleIds);
                throw new ServiceException(MSG.ER_403);
            }
        }

        //要求必须全部具备的角色
        ArrayList<String> mastHasRoleIds = new ArrayList<>(ArrayKit.asList(auth.role()));
        for (String mastHasRoleId : mastHasRoleIds) {
            if (!userRoleIds.contains(mastHasRoleId)) {
                log.error("【权限不足：不具备角色：{}】 requestURI:{}  userAccount:{}  userRoleIds:{}", mastHasRoleId, requestURI, currentUser.getAccount(), userRoleIds);
                throw new ServiceException(MSG.ER_403);
            }
        }

        //没有直接放行，需要继续判断
        return false;
    }


//    public void mastHasRole(String requestURI, AuthUser currentUser) {
//        List<String> userRoleIds = currentUser.getRoleIds();
//
//        HashMap<String, String> resMastRole = ablSecurityProperties.getResMastRole();
//
//        Set<String> allRes = resMastRole.keySet();
//
//        //遍历所有能匹配上的资源
//        for (String resPath : allRes) {
//            boolean match = pathMatcher.match(resPath, requestURI);
//            if (match) {
//                //检查该资源所需的全部角色
//                ArrayList<String> mastHasRoleIds = StringKit.split(resMastRole.get(resPath));
//                //对应该资源，如果用户不具备所有角色，则抛出异常
//                for (String mastHasRoleId : mastHasRoleIds) {
//                    if (!userRoleIds.contains(mastHasRoleId)) {
//                        log.error("权限不足  必须具备角色：{}  requestURI:{}  userAccount:{}  userRoleIds:{}", mastHasRoleId, requestURI, currentUser.getAccount(), userRoleIds);
//                        throw new SrsException(MSG.ER_403);
//                    }
//                }
//            }
//        }
//    }
}
